﻿/*
    Copyright (C) 2009 Shim Manning

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

using System.Net;
using System.IO;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Xml.Serialization;
using HtmlAgilityPack;

namespace Shimpossible.Vonage
{
    /// <summary>
    /// Connect to vonage using their web site
    /// </summary>
    [VonageSchema("http")]
    [VonageSchema("https")]
    public class HttpVonageConnection : VonageConnection
    {
        static string NOT_CONNECTED_ERR = "Please login";

        static ASCIIEncoding encoding = new ASCIIEncoding();        

        static Regex rxAcountNum = new Regex(@"Account[\s(&nbsp;)]+Number:[\s(&nbsp;)]+(\d+)", RegexOptions.Compiled);

        CookieContainer cookies = new CookieContainer();

        /// <summary>
        /// Create a new connection using the default base url
        /// https://secure.vonage.com
        /// </summary>
        public HttpVonageConnection()
            : this(null)
        {
        }
        /// <summary>
        /// Open a new connection to the server
        /// </summary>
        /// <param name="baseUrl">Base url of sight NULL defaults to secure.vonage.com</param>
        /// <param name="user">user name</param>
        /// <param name="p"></param>
        public HttpVonageConnection(string baseUrl) : base(baseUrl)
        {
            if(baseUrl == null)
                this.baseUrl = "https://secure.vonage.com";

            ServicePointManager.ServerCertificateValidationCallback =
                 delegate(
                     object sender,
                     X509Certificate certificate,
                     X509Chain chain,
                     SslPolicyErrors sslPolicyErrors)
                 {
                     return true;
                 };
        }

        /// <summary>
        /// Logout 
        /// </summary>
        public override void Close()
        {
            GetUrl("/webaccount/public/logoff.htm");
        }

        /// <summary>
        /// Connect to the website and login
        /// </summary>
        /// <param name="username">user name</param>
        /// <param name="password">password</param>
        public override void Open(string username, string password)
        {
            string postData = "username=" +username+ "&password=" + password;

            // login
            WebResponse rs = PostUrl("/webaccount/public/login.htm", postData);

            // try to go to dashboard
            StreamReader rd = new StreamReader(GetUrl("/webaccount/dashboard/index.htm").GetResponseStream());
            String html = rd.ReadToEnd();

            
            // did we get a message saying we need to login?
            if (NeedLogin(html))
                throw new InvalidOperationException("Login not correct");

            Match m = rxAcountNum.Match(html);
            this.accountNumber = long.Parse(m.Groups[1].Value);                                                
        }


        /// <summary>
        /// Retrieve list of voice mail
        /// </summary>
        /// <returns>list of voice mail</returns>
        /// <seealso cref="VoiceMail"/>
        override public List<VoiceMail> Voicemail()
        {
            Stream stream = GetUrl("/webaccount/features/voicemail/messages/view.htm").GetResponseStream(); 
            StreamReader r = new StreamReader(stream);

            HtmlDocument doc = new HtmlDocument();
            string html = r.ReadToEnd();

            // check if we got the 'please login' page..
            if (NeedLogin(html))
                throw new InvalidOperationException(NOT_CONNECTED_ERR);
    
            doc.LoadHtml(html);

            HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//tr[td[1]/input]");
            
            List<VoiceMail> mail = new List<VoiceMail>();
            for (int i = 1; i < nodes.Count; i++)
            {
                HtmlNode n = nodes[i];

                // 1st TD/INPUT/@value
                int id = int.Parse(n.ChildNodes[1].ChildNodes[0].Attributes["value"].Value);
                string vmUrl = n.ChildNodes[11].ChildNodes[0].Attributes["href"].Value;
                vmUrl = System.Web.HttpUtility.HtmlDecode(vmUrl);

                // Jul 1, 2009 11:03 AM
                DateTime date = DateTime.ParseExact(
                    n.ChildNodes[7].InnerText,
                    "MMM d, yyyy h:mm tt",
                    System.Globalization.CultureInfo.InvariantCulture
                );

                mail.Add( new HttpVoiceMail(this,id, n.ChildNodes[5].InnerText, 
                    date,
                    ToTimeSpan(n.ChildNodes[9].InnerText),
                    vmUrl)
                    );
            }

            return mail;            
        }

        /// <summary>
        /// Request a page using POST method
        /// </summary>
        /// <param name="url">url to request</param>
        /// <param name="txt">Text to use for POST data</param>
        /// <returns>response from page</returns>
        public WebResponse PostUrl(string url, string txt)
        {
            HttpWebRequest re = (HttpWebRequest)WebRequest.Create(baseUrl + url);
            re.CookieContainer = cookies;
            re.AllowAutoRedirect = true;
            re.AllowWriteStreamBuffering = true;

            byte[] data = encoding.GetBytes(txt);
            re.Method = "POST";
            re.ContentType = "application/x-www-form-urlencoded";
            re.ContentLength = data.Length;
            re.GetRequestStream().Write(data, 0, data.Length);

            return re.GetResponse();
        }

        /// <summary>
        /// Request a page using GET method
        /// </summary>
        /// <param name="url">url to request</param>
        /// <returns>response from page</returns>
        public WebResponse GetUrl(string url)
        {
            HttpWebRequest re = (HttpWebRequest)WebRequest.Create(baseUrl + url);
            re.CookieContainer = cookies;
            re.AllowAutoRedirect = true;
            re.AllowWriteStreamBuffering = true;

            return re.GetResponse();
        }
       
        private TimeSpan ToTimeSpan(string s)
        {
            string[] parts = s.Split(new char[]{':'});

            return TimeSpan.FromSeconds(int.Parse(parts[0]) * 60 + int.Parse(parts[1]));
        }

        /// <summary>
        /// Retrieve list of Activity on account
        /// </summary>
        /// <returns>a enumerable of CallRecords</returns>
        /// <seealso cref="CallRecord"/>
        override public IEnumerable<CallRecord> Activity()
        {

            long SEVEN_DAYS = 604800000;    // number of millseconds in 7 days
            long dateEnd;
            long dateStart = Utils.ToUnixMills(DateTime.Now);
            string dateString;

            // timeZone=0 will give GMT time
            string url = "/vonage-web/activity/activityDetail.htm?" +
                "accountNumber="+this.accountNumber+"&" +
                "type=xml&timeZone=0&locale=en_US&" +
                //"didNumberId="+fone+
                "&time="+dateStart;

            XmlSerializer s = new XmlSerializer(typeof(VonageResponse));

            while (true)
            {
                dateEnd = dateStart;
                dateStart -= SEVEN_DAYS;
                dateString = "dateEnd=" + dateEnd + "&dateStart=" + dateStart;

                WebResponse resp = GetUrl(url + "&" + dateString);

                string txt = new StreamReader(resp.GetResponseStream()).ReadToEnd();
                if (NeedLogin(txt))
                    throw new InvalidOperationException(NOT_CONNECTED_ERR);

                VonageResponse res = (VonageResponse)s.Deserialize(new StringReader(txt));
                //no records so return
                if (res.callrecords.Length == 0) break;
                foreach (CallRecord cr in res.callrecords)
                {
                    yield return cr;
                }

            }
        }

        /// <summary>
        /// Checks text if it contains the error text
        /// asking the user to login
        /// </summary>
        /// <param name="txt">html of the page</param>
        /// <returns></returns>
        private bool NeedLogin(string txt)
        {
            return txt.Contains("Please log in to access your Account");
        }
    }
}
